package zdream.control.foe.mm;

import java.util.Objects;
import java.util.function.BooleanSupplier;

import zdream.control.world.Box;
import zdream.control.world.buff.ControlModule;
import zdream.control.world.MFoe;
import zdream.control.world.MFoeBuff;
import zdream.control.world.buff.HealthModule;
import zdream.control.world.MHookType;
import zdream.control.world.MHooks;
import zdream.control.world.TerrainHandler;
import zdream.control.world.Ticker;

import static zdream.control.foe.mm.MegaManConstant.*;
import static zdream.control.world.MFoeConstants.CATEGORY_MOTION;

/**
 * @author Zdream
 * @since 0.0.1
 * @date 2022-06-03 (last modified)
 */
public class RushJetMotionModule extends MFoeBuff {
	public final RushJet owner;

	public RushJetMotionModule(RushJet owner) {
		super(owner);
		this.owner = Objects.requireNonNull(owner);
	}

	@Override
	public String getCategory() {
		return CATEGORY_MOTION;
	}

	@Override
	public void init() {
		HealthModule healthModule = owner.healthModule;
		if (healthModule == null) {
			throw new IllegalStateException("RushJetMotionModule 无法找到 RushJet 的 HealthModule, 无法重置出界判定");
		}
		healthModule.outsideChecker = this::isOutside;

		owner.terrainHandler = new TerrainHandler(owner.world);
		checkBox.side = owner.side;

		foe.onClean(f -> destroyBeforeMotionHandlerIfExists());
		onHook(MHookType.TICK)
				.handle(this::tick)
				.buildAndSave();
	}

	public void tick(MHooks.MHookContext rawContext) {
		Ticker ticker = owner.world.ticker;
		Box box = owner;
		box.velocityX = 0;
		box.velocityY = 0;

		TerrainHandler tHandler = owner.terrainHandler;
		if (prepareDuration > 0) {
			prepareDuration -= ticker.lDuration;

			if (tHandler.isBoxOverlap(box) && prepareDuration < 2) {
				System.err.println("这个地方放不了飞狗");
				owner.destroy();
			}
		} else {
			if (tHandler.isBoxOverlap(box)) {
				boolean success = tHandler.glitchFix(box, true, true, true, true);
				if (!success) {
					System.err.println("这个地方放不了飞狗");
					owner.destroy();
					return;
				}
			}

			box.setTerrainWithSide(
					box.gravityDown ? Box.BOX_TERRAIN_PLATFORM_UP : Box.BOX_TERRAIN_PLATFORM_DOWN, box.side);
			boolean stand = ownerStand();

			if (stand) {
				flying = true;
				createBeforeMotionHandlerIfNotExists();
				ownerStandPositionFix(ticker);
			} else {
				destroyBeforeMotionHandlerIfExists();
			}

			if (flying) {
				MegaMan mm = this.owner.owner.owner;
				ControlModule controlModule = mm.controlModule;

				// 上或者下的判定
				boolean pressUp = controlModule.up;
				boolean pressDown = controlModule.down;

				if (stand && (pressUp || pressDown)) {
					float deltaY = RUSHJET_VY * ticker.duration;
					deltaY = checkVy(pressUp ? deltaY : -deltaY);
					box.setVelocityY(deltaY);
				}

				// 减速的判定
				boolean pressSlowDown = stand
						&& (box.orientation && controlModule.left
						|| !box.orientation && controlModule.right);
				float vx = RUSHJET_VX * ticker.duration;
				if (pressSlowDown) {
					box.setVelocityX(box.orientation ? vx * 0.5f : -vx * 0.5f);
				} else {
					box.setVelocityX(box.orientation ? vx : -vx);
				}

				tHandler.submitMotion(box);
			}
		}
	}

	/* **********
	 *   行为   *
	 ********** */

	/**
	 * 召唤飞狗之后 0.2 秒到指定位置成型.
	 * 开始时, 每 step 减一. 到 0 时准备阶段结束.
	 *
	 * 在准备阶段, 飞狗不能踩的.
	 */
	public long prepareDuration = 200;
	/**
	 * 是否开始飞
	 */
	public boolean flying = false;
	/**
	 * 用于判定 RushJet 最高到哪里的 box, 不是 Foe 真正的 box.
	 */
	Box checkBox = new Box();

	/**
	 * 洛克站在飞狗上的位置进行 Fix, 直至洛克站到飞狗的正上方
	 */
	private void ownerStandPositionFix(Ticker ticker) {
		MegaMan mm = this.owner.owner.owner;
		float mmX = mm.anchorX;
		float rushX = this.owner.anchorX;
		if (mmX == rushX) {
			return;
		}

		if (mmX < rushX) {
			mm.motionModule.submitMotion(Math.min(RUSHJET_STAND_POSITION_FIX_VX * ticker.duration, rushX - mmX), 0);
		} else {
			mm.motionModule.submitMotion(Math.max(-RUSHJET_STAND_POSITION_FIX_VX * ticker.duration, rushX - mmX), 0);
		}
	}

	private boolean isOutside(MFoe foe) {
		MegaMan mm = this.owner.owner.owner;
		float distance = Math.abs(mm.anchorX - owner.anchorX) + Math.abs(mm.anchorY - owner.anchorY);
		return distance >= 12 || (!ownerStand() && foe.world.isOutsideRoom(foe));
	}

	/**
	 * @return
	 *   是否洛克站在上面
	 */
	private boolean ownerStand() {
		MegaMan mm = this.owner.owner.owner;
		return this.owner.world.stand(mm, owner);
	}

	/**
	 * 因为洛克乘着 RushJet 按上向偏上方飞, 碰到天花板后因为 glitch fix,
	 * 洛克会强制脱离 RushJet 掉下来, 所以要检查移动的 y 轴位移,
	 * 避免发生这种事情.
	 */
	private float checkVy(float deltaY) {
		Box ownerBox = owner;
		if (!ownerBox.gravityDown) {
			throw new IllegalArgumentException("TODO 乘 RushJet gravityDown=false 没完成");
		}

		checkBox.setBox(ownerBox.boxX, ownerBox.boxY, ownerBox.boxWidth, ownerBox.boxHeight + 1.5f);
		checkBox.setAnchor(ownerBox.anchorX, ownerBox.anchorY);
		owner.terrainHandler.submitMotion(checkBox, 0, deltaY);
		return checkBox.anchorY - ownerBox.anchorY;
	}

	/* **********
	 *   事件   *
	 ********** */
	private BooleanSupplier mmStandEventCleaner;

	private void createBeforeMotionHandlerIfNotExists() {
		if (mmStandEventCleaner != null) {
			return;
		}
		mmStandEventCleaner =
				owner.owner.owner.motionModule.addBeforeMoveExtension(this::handleMegaMenBeforeMotion);
	}

	private void destroyBeforeMotionHandlerIfExists() {
		if (mmStandEventCleaner != null) {
			mmStandEventCleaner.getAsBoolean();
			mmStandEventCleaner = null;
		}
	}

	/**
	 * 当站在 RushJet 上后, 所有的 motion 判定都失效
	 */
	private void handleMegaMenBeforeMotion(MFoe foe) {
		if (!(foe instanceof MegaMan)) {
			return;
		}
		((MegaMan) foe).motionModule.motionEnable = false;
	}
}
